
package holdem.infengine;

import holdem.*;
import holdem.Rulegui.Action;
import holdem.Rulegui.GeneticCode;
import holdem.Rulegui.Loader;
import holdem.Rulegui.Rule;


import holdem.network.Client;

import holdem.pokergui.MessageReceiverInGUI;
import holdem.pokergui.StartForm;

import holdem.workingMemory.WorkingMemory;

//~--- imports: JDK -----------------------------------------------------------
import java.io.IOException;

import java.util.*;
import javax.swing.JOptionPane;


/**
 *
 * @author Pai Qi
 * @author qipai0@gmail.com
 * Description:
 * Start Date:
 *
 */

public class InferenceEngine
	implements MessageReceiverInGUI {
    public static final InferenceEngine me = new InferenceEngine();

    
    private GeneticCode gc;
    
    private Integer      self = null;
    private Integer lastBetAmount = 0;
    private Client client;

    Vector<Rule> betRules;
    Vector<Rule> emotionRules;
    Loader loader;

    // ==========================
    // Active Mode

    //~--- methods ------------------------------------------------------------

    public void decideAction() {

        // initialize rule base
        if(loader == null) {
            loader = new Loader();
            loader.generate(WorkingMemory.me, loader);
        }
        if(betRules == null) {
            betRules = loader.getBetRule();
        }


        Rule bestRule = null;
        for(Rule r : betRules) {
            if(r.preondition(loader.getCode(), WorkingMemory.me)) {
                if(bestRule == null) {
                    bestRule = r;
                } else {
                    bestRule = bestRule.getPri().compareTo(r.getPri()) < 0 ? r : bestRule;
                }
            }
        }

        Action ourAction = new Action();
        bestRule.exicute(loader.getCode(), WorkingMemory.me, ourAction);
        try {
            try {
                if(ourAction.bet != 0) {
                    int amount = Math.abs(ourAction.bet);
                    int maxBet = findPlayerWithID(self).moneyAtCurrent;
                    int minBet = lastBetAmount;

                    if(minBet > amount) {
                        client.fold();
                    } else if(amount > maxBet) {
                        client.bet(maxBet);
                    } else {
                        client.bet(amount);
                    }
                } else if(ourAction.check != 0) {
                    client.check();
                } else if(ourAction.fold != 0) {
                    client.fold();
                }

            } catch(Exception e) {
                client.fold();
            }
        } catch(Exception e) {}

        


//        int maxAmountCanBet = findPlayerWithID(self).moneyAtCurrent;
//        try {
//            if(maxAmountCanBet >= lastBetAmount) {
//                client.check();
//            } else {
//                client.fold();
//            }
//        } catch (IOException ex) {
//            System.err.println("Cannot make action?");
//        }
    }

    // ==========================
    // Listening Mode


    public void bet(Integer playerID, Integer amount) {
	Player p = findPlayerWithID(playerID);

	p.moneyAtCurrent          -= (amount - p.moneyBet);
        WorkingMemory.me.potMoney += (amount - p.moneyBet);
        p.moneyBet = amount;
        lastBetAmount = amount;
        p.raiseCount++;
	p.lastEvent               = Player.PlayerEvent.raise;
    }

    public void fold(Integer playerID) {
	Player p = findPlayerWithID(playerID);
        p.foldCount++;
	p.lastEvent = Player.PlayerEvent.fold;
	p.isFolded  = true;
    }

    public void quit(Integer playerID) {
	Player p = findPlayerWithID(playerID);

	p.lastEvent = Player.PlayerEvent.fold;
	p.isFolded  = true;
        p.hasQuit  = true;
	WorkingMemory.me.playingOrder.remove(findPlayerWithID(playerID));
    }

    public void check(Integer playerID) {
	Player p = findPlayerWithID(playerID);

        int amount = lastBetAmount;
        
        p.moneyAtCurrent          -= (amount - p.moneyBet);
        WorkingMemory.me.potMoney += (amount - p.moneyBet);
        p.moneyBet = amount;
        lastBetAmount = amount;
	if(amount == p.moneyBet) {
            p.lastEvent               = Player.PlayerEvent.check;
            p.checkCount++;
        } else {
            p.lastEvent               = Player.PlayerEvent.call;
            p.foldCount++;
        }

    }

    public void showDown() {
	
    }

    public void revealPlayerHand(Map<Integer, List<Card>> cardMap) {

	for (Integer i : cardMap.keySet()) {
	    Player p = findPlayerWithID(i);

	    p.handCardsAvailable = true;
	    p.handCards.clear();
	    p.handCards.addAll(cardMap.get(i));
	}

    }

    private Player findPlayerWithID(Integer id) {
	for (Player p : WorkingMemory.me.playingOrder) {
	    if (p.id.equals(id)) {
		return p;
	    }
	}

	return null;
    }


    public void dealTableCard(Card newTableCard) {

	WorkingMemory.me.tableCards.add(newTableCard);
        WorkingMemory.me.currentPhase = WorkingMemory.me.tableCards.size();

    }

    public void moneyGain(Integer playerID, Integer amount) {
	findPlayerWithID(playerID).moneyAtCurrent += amount;
    }

    Integer currentTurnID;
    public void changeTurn(Integer tableLocation) {

        if(loader == null) {
            loader = new Loader();
            loader.generate(WorkingMemory.me, loader);
        }
        if(emotionRules == null) {
            emotionRules = loader.getRuleEm();
        }

        Rule bestRule = null;
        for(Rule r : emotionRules) {
            if(r.preondition(loader.getCode(), WorkingMemory.me)) {
                if(bestRule == null) {
                    bestRule = r;
                } else {
                    bestRule = bestRule.getPri().compareTo(r.getPri()) < 0 ? r : bestRule;
                }
            }
        }

        bestRule.exicute(loader.getCode(), WorkingMemory.me, findPlayerWithID(currentTurnID).getEmotion());



        
        currentTurnID = tableLocation;
	//WorkingMemory.me.playingOrder.add(WorkingMemory.me.playingOrder.remove(0));

	if (currentTurnID.equals(self)) {

	    // change to active mode
            decideAction();
            // back to listening mode
	}
    }

//    public void newPlayer(
//	    Integer playerID, String playerName, Integer tableLocation,
//	    Integer money) {
//
//	// Assumption: first newPlayer is myself
//	Player p = new Player(playerName, playerID, money);
//        p.tableLocation = tableLocation;
//	WorkingMemory.me.playingOrder.add(p);
//
//
//    }

    private boolean firstRun = true;
    private void setPlayOrder() {
        // sort playOrder by tableLocation
	Player[] players = new Player[8];

	for (Player p : WorkingMemory.me.playingOrder) {
	    players[p.tableLocation] = p;
	}

	WorkingMemory.me.playingOrder.clear();

	for (int tableLoc = 0; tableLoc < 8; tableLoc++) {
	    if (players[tableLoc] != null) {
		WorkingMemory.me.playingOrder.add(players[tableLoc]);
	    }
	}
    }
    public void gameStart() {
        if(firstRun) {
            firstRun = false;
            
        }
	WorkingMemory.me.clear();

	
    }

    public void gameEnd() {
        WorkingMemory.me.potMoney = 0;
    }

    public static InferenceEngine get() {
        return me;
    }

    public void joinAccpted(Integer playerID) {
        self = playerID;

    }

    public void dealHandCard(Card[] handCards) {
        findPlayerWithID(self).handCardsAvailable = true;
        findPlayerWithID(self).handCards.add(handCards[0]);
        findPlayerWithID(self).handCards.add(handCards[1]);
    }

    public void newPlayer(Integer playerID, String playerName, Integer money) {
        Player p = new Player(playerName, playerID, money);
        p.tableLocation = playerID;
	WorkingMemory.me.playingOrder.add(p);
    }

    public void smallBlindIncrease(Integer playerID, Integer newAmount) {
        WorkingMemory.me.currentSmallBlind = newAmount;
        bet(playerID, newAmount);
    }

    public void bigBlindIncrease(Integer playerID, Integer newAmount) {
        WorkingMemory.me.currentBigBlind = newAmount;
        bet(playerID, newAmount);
    }

    public void dealerChange(Integer playerID) {
        //setPlayOrder();
        WorkingMemory.me.currentDealerID = playerID;
        for(Player p : WorkingMemory.me.playingOrder) {
            p.moneyBet = 0;
        }
    }

    public static void main(String[] args)  {
        try {
            InferenceEngine ie = new InferenceEngine();
            Client client = new Client(ie);
            ie.client = client;
            StartForm frm = new StartForm();


            JOptionPane.showConfirmDialog(null, frm, "Start Info", JOptionPane.DEFAULT_OPTION);

            client.initConnection(frm.getIP(), 3333);
            client.letMeJoin(frm.getName(), frm.getMoney());
        } catch (IOException ex) {

        }
    }
}
